﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using AForge.Imaging.Filters;
using HomeDmsUtils;

namespace HomeDms.Imaging
{
    public partial class CropToolPanel : ImageToolPanel
    {
        public CropToolPanel()
        {
            InitializeComponent();
        }

        struct ResizeCursor
        {
            internal GrabHandle grab;
            internal Cursor cursor;
        }

        ResizeCursor[] cursorByMove = new ResizeCursor[9];
        enum GrabHandle
        {
            None = 0,
            Left = 1,
            Up = 2,
            Right = 3,
            Down = 4,
            LeftUp = 5,
            RightUp = 6,
            RightDown = 7,
            LeftDown = 8
        }

        GrabHandle grabHandle = GrabHandle.None;

        private void CropToolPanel_Load(object sender, EventArgs e)
        {
            cursorByMove[0].grab = GrabHandle.None;
            cursorByMove[0].cursor = Cursors.Default;
            cursorByMove[1].grab = GrabHandle.Left;
            cursorByMove[1].cursor = Cursors.SizeWE;
            cursorByMove[2].grab = GrabHandle.Up;
            cursorByMove[2].cursor = Cursors.SizeNS;
            cursorByMove[3].grab = GrabHandle.Right;
            cursorByMove[3].cursor = Cursors.SizeWE;
            cursorByMove[4].grab = GrabHandle.Down;
            cursorByMove[4].cursor = Cursors.SizeNS;
            cursorByMove[5].grab = GrabHandle.LeftUp;
            cursorByMove[5].cursor = Cursors.SizeNWSE;
            cursorByMove[6].grab = GrabHandle.RightUp;
            cursorByMove[6].cursor = Cursors.SizeNESW;
            cursorByMove[7].grab = GrabHandle.RightDown;
            cursorByMove[7].cursor = Cursors.SizeNWSE;
            cursorByMove[8].grab = GrabHandle.LeftDown;
            cursorByMove[8].cursor = Cursors.SizeNESW;
            PictureBox.MouseDown += new MouseEventHandler(PictureBox_MouseDown);
            PictureBox.MouseMove += new MouseEventHandler(PictureBox_MouseMove);
            PictureBox.MouseUp += new MouseEventHandler(PictureBox_MouseUp);
            PictureBox.Paint += new PaintEventHandler(PictureBox_Paint);
            PictureBox.SizeChanged += new EventHandler(PictureBox_SizeChanged);
        }

        void PictureBox_SizeChanged(object sender, EventArgs e)
        {
            RecalculateRectangle(new Point(rect.X, rect.Y));
            PictureBox.Invalidate();
        }

        const float marginPercent = 0.1f;
        
        private int R(float f)
        {
            return (int)Math.Round(f);
        }

        public override QualityPictureBox PictureBox
        {
            get
            {
                return base.PictureBox;
            }
            set
            {
                base.PictureBox = value;
                Size zsize = PictureBox.OriginalImage.GetZoomedSize(PictureBox.ClientSize);
                float x = (PictureBox.ClientSize.Width - zsize.Width) / 2 + marginPercent * zsize.Width;
                float y = (PictureBox.ClientSize.Height - zsize.Height) / 2 + marginPercent * zsize.Height;
                float w = zsize.Width * (1 - 2 * marginPercent);
                float h = zsize.Height * (1 - 2 * marginPercent);
                rect = new Rectangle(R(x), R(y), R(w), R(h));
                PictureBox.Invalidate();
            }
        }

        const int halfGrab = 5; // pix
        const int fullGrab = halfGrab * 2; // pix

        void PictureBox_Paint(object sender, PaintEventArgs e)
        {
            Pen pen = Pens.Black.Clone() as Pen;
            pen.DashStyle = DashStyle.Dash;
            e.Graphics.DrawRectangle(pen, rect);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.Left), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.Up), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.RightUp), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.Right), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.RightDown), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.Down), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.LeftDown), true, true);
            ControlPaint.DrawGrabHandle(e.Graphics, CalcRect(GrabHandle.LeftUp), true, true);            
        }

        void PictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            mouseDown = false;
            PictureBox.Cursor = Cursors.Default;
        }

        Rectangle rect;

        void PictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (mouseDown && grabHandle != GrabHandle.None)
            {
                RecalculateRectangle(e.Location);
                PictureBox.Invalidate();
            }
            else if (!mouseDown)
            {
                PictureBox.Cursor = cursorByMove[(int)GetOverGrabHandle(e.Location)].cursor;
            }
        }

        private void RecalculateRectangle(Point p)
        {
            Point oldloc1 = new Point(rect.Left, rect.Top);
            Point oldloc2 = new Point(rect.Right, rect.Bottom);
            Point newloc1 = new Point();
            Point newloc2 = new Point();
            newloc1 = oldloc1;
            newloc2 = oldloc2;
            switch (grabHandle)
            {
                case GrabHandle.Left:
                    newloc1.X = p.X;
                    break;
                case GrabHandle.Right:
                    newloc2.X = p.X;
                    break;
                case GrabHandle.Up:
                    newloc1.Y = p.Y;
                    break;
                case GrabHandle.Down:
                    newloc2.Y = p.Y;
                    break;
                case GrabHandle.LeftUp:
                    newloc1.X = p.X;
                    newloc1.Y = p.Y;
                    break;
                case GrabHandle.RightDown:
                    newloc2.X = p.X;
                    newloc2.Y = p.Y;
                    break;
                case GrabHandle.LeftDown:
                    newloc1.X = p.X;
                    newloc2.Y = p.Y;
                    break;
                case GrabHandle.RightUp:
                    newloc2.X = p.X;
                    newloc1.Y = p.Y;
                    break;
            }

            Size zsize = PictureBox.OriginalImage.GetZoomedSize(ClientSize);
            int deltaX = (PictureBox.ClientSize.Width - zsize.Width) / 2;
            int deltaY = (PictureBox.ClientSize.Height - zsize.Height) / 2;
            // Right, Bottom boundary checks (1)
            newloc1.X = newloc1.X > deltaX + zsize.Width ? deltaX + zsize.Width - 1 : newloc1.X;
            newloc1.Y = newloc1.Y > deltaY + zsize.Height ? deltaY + zsize.Height - 1 : newloc1.Y;
            // Left, Top boundary checks (1)
            newloc1.X = newloc1.X < deltaX ? deltaX : newloc1.X;
            newloc1.Y = newloc1.Y < deltaY ? deltaY : newloc1.Y;
            // Right, Bottom boundary checks (2)
            newloc2.X = newloc2.X > deltaX + zsize.Width ? deltaX + zsize.Width : newloc2.X;
            newloc2.Y = newloc2.Y > deltaY + zsize.Height ? deltaY + zsize.Height : newloc2.Y;
            // Left, Top boundary checks (2)
            newloc2.X = newloc2.X < deltaX ? deltaX : newloc2.X;
            newloc2.Y = newloc2.Y < deltaY ? deltaY : newloc2.Y;
            // Cross checks (1)
            newloc1.X = newloc1.X >= newloc2.X - 1 ? newloc2.X - 2 : newloc1.X;
            newloc1.Y = newloc1.Y >= newloc2.Y - 1 ? newloc2.Y - 2 : newloc1.Y;
            // Cross checks (2)
            newloc2.X = newloc2.X <= newloc1.X + 1 ? newloc1.X + 2 : newloc2.X;
            newloc2.Y = newloc2.Y <= newloc1.Y + 1 ? newloc1.Y + 2 : newloc2.Y;

            rect = new Rectangle(newloc1.X, newloc1.Y, newloc2.X - newloc1.X, newloc2.Y - newloc1.Y);
        }

        bool mouseDown = false;

        void PictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                mouseDown = true;
                SetMoveDirection(e.Location);
            }
        }

        private void SetMoveDirection(Point point)
        {
            grabHandle = GetOverGrabHandle(point);
            PictureBox.Cursor = cursorByMove[(int)grabHandle].cursor;
            PictureBox.Invalidate();
        }

        private GrabHandle GetOverGrabHandle(Point point)
        {
            if (CalcRect(GrabHandle.Down).Contains(point))
                return GrabHandle.Down;
            if (CalcRect(GrabHandle.Left).Contains(point))
                return GrabHandle.Left;
            if (CalcRect(GrabHandle.LeftDown).Contains(point))
                return GrabHandle.LeftDown;
            if (CalcRect(GrabHandle.LeftUp).Contains(point))
                return GrabHandle.LeftUp;
            if (CalcRect(GrabHandle.Right).Contains(point))
                return GrabHandle.Right;
            if (CalcRect(GrabHandle.RightDown).Contains(point))
                return GrabHandle.RightDown;
            if (CalcRect(GrabHandle.RightUp).Contains(point))
                return GrabHandle.RightUp;
            if (CalcRect(GrabHandle.Up).Contains(point))
                return GrabHandle.Up;
            return GrabHandle.None;
        }

        private Rectangle CalcRect(GrabHandle grabHandle)
        {
            int X1 = rect.Left;
            int Y1 = rect.Top;
            int X2 = rect.Right;
            int Y2 = rect.Bottom;
            int Wx05 = (int)Math.Round(rect.Width / 2f);
            int Hx05 = (int)Math.Round(rect.Height / 2f);
            switch (grabHandle)
            {
                case GrabHandle.Down:
                    return new Rectangle(X1 + Wx05 - halfGrab, Y2 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.Left:
                    return new Rectangle(X1 - halfGrab, Y1 + Hx05 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.LeftDown:
                    return new Rectangle(X1 - halfGrab, Y2 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.LeftUp:
                    return new Rectangle(X1 - halfGrab, Y1 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.Right:
                    return new Rectangle(X2 - halfGrab, Y1 + Hx05 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.RightDown:
                    return new Rectangle(X2 - halfGrab, Y2 - halfGrab, fullGrab, fullGrab);
                case GrabHandle.RightUp:
                    return new Rectangle(X2 - halfGrab, Y1 - halfGrab, fullGrab, fullGrab);;
                case GrabHandle.Up:
                    return new Rectangle(X1 + Wx05 - halfGrab, Y1 - halfGrab, fullGrab, fullGrab);
                default:
                    return new Rectangle(0, 0, 0, 0);
            }
        }

        protected override void FinalApply()
        {
            Size zsize = PictureBox.OriginalImage.GetZoomedSize(ClientSize);
            float rate = zsize.Width / (float)PictureBox.OriginalImage.Width;
            Rectangle realRect = new Rectangle((int)Math.Round(rect.X / rate), (int)Math.Round(rect.Y / rate), 
                (int)Math.Round(rect.Width / rate), (int)Math.Round(rect.Height / rate));
            Crop crop = new Crop(realRect);
            Bitmap = crop.Apply(Bitmap);
            PictureBox.Invalidate();
        }

        private void CropToolPanel_VisibleChanged(object sender, EventArgs e)
        {
            if (!Visible)
            {
                PictureBox.MouseDown -= new MouseEventHandler(PictureBox_MouseDown);
                PictureBox.MouseMove -= new MouseEventHandler(PictureBox_MouseMove);
                PictureBox.MouseUp -= new MouseEventHandler(PictureBox_MouseUp);
                PictureBox.Paint -= new PaintEventHandler(PictureBox_Paint);
            }
        }
    }
}
